package cn.dengta.webapp.ai.model;

import java.io.Serializable;
import java.util.Arrays;
import java.util.function.Function;
import java.util.stream.Collectors;

import cn.dengta.common.context.I18N;
import cn.dengta.common.model.ValueProxyImpl;
import cn.dengta.context.model.BaseI18nKey;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.annotation.JSONField;
import lombok.*;
import me.codeplayer.util.*;

@Getter
@Setter
@NoArgsConstructor
public class AIGCVoice implements Serializable {

	private String voiceId;

	/**
	 * 音色相关信息，非必填项，当提供时将覆盖`voice_id`参数。
	 * 包含多个音色及其对应权重的集合。
	 */
	private TimberWeight[] timberWeights;

	/**
	 * 生成声音的语速，非必填项，默认值为1.0，取值范围在[0.5, 2]之间，
	 * 值越大语速越快。
	 */
	private Double speed = 1.0;

	/**
	 * 生成声音的音量，非必填项，默认值为1.0，取值范围在(0, 10]之间，
	 * 值越大音量越高。
	 */
	private Double volume = 1.0;

	/**
	 * 生成声音的语调，非必填项，默认值为0（即原始音色输出），
	 * 取值范围在[-12, 12]之间且需为整数。
	 */
	private Integer pitch = 0;

	/**
	 * 期望生成声音的文本内容，必填项，长度限制小于500个字符。
	 * 如需控制语音间隔时间，可在字间添加<#x#>格式，x单位为秒，支持0.01-99.99s，最多两位小数。
	 */
	private String text;

	private String stdAuditionUrl;

	@Getter
	@Setter
	// TimberWeights 子类
	public static class TimberWeight implements Serializable {

		/**
		 * 音色编号，用于混合音色时指定音色，必填项，参考上文voice_id的说明。
		 */
		private String voiceId;

		/**
		 * 指定音色在混合中的权重，必填项，取值范围在[1, 100]之间，
		 * 单一音色取值占比越高，合成音色越接近该音色。
		 * 最多支持4种音色混合。
		 */
		private int weight;

		public String getLabel() {
			return ValueProxyImpl.label(AIGCVoiceId.of(voiceId));
		}

		@Override
		public String toString() {
			return voiceId + ":" + weight;
		}

	}

	public String md5() {
		String voiceIdStr = X.isValid(timberWeights) ? StringUtil.join(Arrays.stream(timberWeights).map(TimberWeight::toString).collect(Collectors.toList()), Function.identity(), ",")
				: voiceId;
		return Encrypter.md5(voiceIdStr + ":"
				+ speed + ":"
				+ volume + ":"
				+ pitch + ":"
				+ "mp3:"
				+ text);
	}

	public void validate() {
		Assert.isTrue(StringUtil.notEmpty(text), "text not empty");

		boolean hasVoiceId = X.isValid(voiceId);
		boolean hasTimberWeights = X.isValid(timberWeights);

		Assert.isTrue(hasVoiceId || hasTimberWeights, "One of voiceId or timberWeights is required");

		if (hasVoiceId) {
			Assert.isTrue(X.isValid(AIGCVoiceId.of(voiceId)), "voiceId not exist");
		}
		if (hasTimberWeights) {
			for (TimberWeight timberWeight : timberWeights) {
				Assert.isTrue(StringUtil.notEmpty(timberWeight.voiceId) && X.isValid(AIGCVoiceId.of(timberWeight.voiceId)), "voiceId not exist");
				Assert.isTrue(timberWeight.weight > 0 && timberWeight.weight <= 100, "weight between (0,100]");
			}
		}
		if (speed != null) {
			Assert.isTrue(speed >= 0.5 && speed <= 2, "speed between [0.5,2]");
		}
		if (volume != null) {
			Assert.isTrue(volume > 0 && volume <= 10, "volume between (0, 10]");
		}
		if (pitch != null) {
			Assert.isTrue(pitch >= -12 && pitch <= 12, "pitch between [-12,12]");
		}

	}

	@JSONField
	public boolean isStdVoice() {
		return (X.size(timberWeights) <= 1) && speed == 1.0 && pitch == 0 && volume == 1.0;
	}

	public String getVoiceId() {
		int size = X.size(timberWeights);
		if (size == 1) {
			return timberWeights[0].getVoiceId();
		} else if (size > 1) {
			return null;
		}
		return voiceId;
	}

	public String getLabel() {
		int size = X.size(timberWeights);
		if (size == 1) {
			return ValueProxyImpl.label(AIGCVoiceId.of(timberWeights[0].getVoiceId()));
		} else if (size == 0) {
			return ValueProxyImpl.label(AIGCVoiceId.of(voiceId));
		}
		return I18N.msg(BaseI18nKey.AIGC_MIXING_VOICE);
	}

	public static AIGCVoice from(String json) {
		if (StringUtil.isEmpty(json)) {
			return null;
		}
		return JSON.parseObject(json, AIGCVoice.class);

	}

}
